﻿using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using TrackClasses;
using System.Threading;

namespace VRacer
{
    public partial class RacePage : PhoneApplicationPage
    {
        // Constructor
        public RacePage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(RacePage_Loaded);
        }

        enum GameMode
        {
            Alone, 
            VSCPU,
            VSHuman,
            CPUVSCPU,
            CPUTest
        }

        private GameMode _gameMode;
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            if (NavigationContext.QueryString.Keys.Contains("mode"))
            {
                if (NavigationContext.QueryString["mode"] == "Alone")
                {
                    MessageBox.Show("Race alone started!");
                    _gameMode = GameMode.Alone;
                }
                if (NavigationContext.QueryString["mode"] == "VSCPU")
                {
                    MessageBox.Show("Race versus CPU started!");
                    _gameMode = GameMode.VSCPU;
                }
                if (NavigationContext.QueryString["mode"] == "VSHuman")
                {
                    MessageBox.Show("Race Human vs Human started!");
                    _gameMode = GameMode.VSHuman;
                }
                if (NavigationContext.QueryString["mode"] == "CPUTest")
                {
                    MessageBox.Show("CPU test started!");   
                    _gameMode = GameMode.CPUTest;
                }
                if (NavigationContext.QueryString["mode"] == "CPUVSCPU")
                {
                    MessageBox.Show("CPU test started!");
                    _gameMode = GameMode.CPUVSCPU;
                }
            }
        }

        System.Windows.Threading.DispatcherTimer moveTimer = new System.Windows.Threading.DispatcherTimer();

        Car _activeCar;

        private Car ActiveCar
        {
            get { return _activeCar; }
            set
            {
                _activeCar = value;
                if (_activeCar.IsCPU)
                {
                    elVelocity.Visibility = Visibility.Collapsed;
                    pupka.Visibility = Visibility.Collapsed;
                } else
                {
                    elVelocity.Visibility = Visibility.Visible;
                    pupka.Visibility = Visibility.Visible;
                }
                //svMap.ScrollToHorizontalOffset(value.CurPosX - 480 / 2);
                //svMap.ScrollToVerticalOffset(value.CurPosY);
            }
        }

        private Road _currentRoad = null;
        private List<Car> CarsOnTrack = new List<Car>(); 
        private Car GetCPUCar()
        {
            Car res = new Car();
            res.IsCPU = true;
            res.Top = _currentRoad.CarsStartPositionY[CarsCount];
            res.Left = _currentRoad.CarsStartPositionX[CarsCount];
            res.InitPosX = res.Left;
            res.InitPosY = res.Top;
            res.elVelocity = elVelocity;
            return res;
        }

        private int CarsCount { get { return CarsOnTrack.Count; } }
        private Car GetHumanCar()
        {
            Car res = new Car();
            res.IsCPU = false;
            res.Top = _currentRoad.CarsStartPositionY[CarsCount];
            res.Left = _currentRoad.CarsStartPositionX[CarsCount];
            res.InitPosX = res.Left;
            res.InitPosY = res.Top;
            res.elVelocity = elVelocity;
            return res;
        }
        void RacePage_Loaded(object sender, RoutedEventArgs e)
        {
            Road r = Road.GetTestRoad();
            _currentRoad = r;

            switch (_gameMode)
            {
                case GameMode.CPUTest:
                    CarsOnTrack.Add(GetCPUCar());
                    break;
                case GameMode.Alone:
                    CarsOnTrack.Add(GetHumanCar());
                    break;
                case GameMode.VSCPU:
                    CarsOnTrack.Add(GetHumanCar());
                    CarsOnTrack.Add(GetCPUCar());
                    break;
                case GameMode.VSHuman:
                    CarsOnTrack.Add(GetHumanCar());
                    CarsOnTrack.Add(GetHumanCar());
                    break;
                case GameMode.CPUVSCPU:
                    CarsOnTrack.Add(GetCPUCar());
                    CarsOnTrack.Add(GetCPUCar());
                    break;
            }
            
            
            moveTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
            moveTimer.Tick += new EventHandler(moveTimer_Tick);
            //   moveTimer.Start();
            
            
            //cnvMap.Children.Add(r.RoadPolygonCached);

            //foreach (var rp in r.RoadParts)
            //{
            //    Line l = new Line();
            //    l.X1 = rp.X1;
            //    l.X2 = rp.X2;
            //    l.Y1 = rp.Y1;
            //    l.Y2 = rp.Y2;
            //    l.Stroke = new SolidColorBrush(Colors.Red);
            //    l.StrokeThickness = 1;
            //    cnvMap.Children.Add(l);
            //}

            //var pathC = r.GetRoadPath();
            //cnvMap.Children.Add(r.GetTrackBitMap());
            imgMap.Source = r.GetTrackBitMap().Source;

            //foreach (var path in pathC)
            //{
            //    cnvMap.Children.Add(path);    
            //}




            cnvMap.Children.Remove(carDefault);

            foreach (var car in CarsOnTrack)
            {
                cnvMap.Children.Add(car);
            }
            //carDefault.elVelocity = elVelocity;
            //carDefault.Top = _currentRoad.CarsStartPositionY[CarsCount];
            //carDefault.Left = _currentRoad.CarsStartPositionX[CarsCount];
            Line finishLine = new Line();

            finishLine.X1 = _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].X1;
            finishLine.X2 = _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].X2;
            finishLine.Y1 = _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].Y1;
            finishLine.Y2 = _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].Y2;
            finishLine.StrokeThickness = 10;
            finishLine.Stroke = new SolidColorBrush(Colors.Black);
            DoubleCollection dc = new DoubleCollection();
            dc.Add(1.0);
            dc.Add(1.0);
            finishLine.StrokeDashArray = dc;
            //finishLine.StrokeDashOffset = 5.0;
            cnvMap.Children.Add(finishLine);


            ActiveCar = CarsOnTrack[0];
            SwitchCars();
            

            svMap.ScrollToHorizontalOffset(ActiveCar.CurPosX - 480 / 2);
            svMap.ScrollToVerticalOffset(ActiveCar.CurPosY);
        }

        private void MakeComputerStep()
        {
            //if (!ActiveCar.IsCPU)
            //    return; //only computer )

            Car1_ManipulationStarted(this, new ManipulationStartedEventArgs());



            lineVectorMovement.X1 = cnvVelocity.Width / 2;
            lineVectorMovement.Y1 = cnvVelocity.Height / 2;

            //lineVectorMovement.X2 = cnvVelocity.Width / 2 + 15;//e.CumulativeManipulation.Translation.X;
            //lineVectorMovement.Y2 = cnvVelocity.Height / 2 - 15;//e.CumulativeManipulation.Translation.Y;

            RoadPart rpCurr = null;
            RoadPart rpCurrNext = null;
            for (int i = 0; i < _currentRoad.RoadParts.Length - 1; i++)
            {
                RoadPart rp = _currentRoad.RoadParts[i];
                RoadPart rpNext = _currentRoad.RoadParts[i + 1];
                PointCollection c = new PointCollection();
                c.Add(new Point(rp.X1, rp.Y1));
                c.Add(new Point(rp.X2, rp.Y2));
                c.Add(new Point(rpNext.X2, rpNext.Y2));
                c.Add(new Point(rpNext.X1, rpNext.Y1));
                if(Road.IsInPolygon2(new Point(ActiveCar.NextPosX, ActiveCar.NextPosY), c))
                {
                    rpCurr = rp;
                    rpCurrNext = rpNext;
                    break;
                }
            }
            
            if((rpCurr == null) || (rpCurrNext == null)) // we will be out of track on next step
            {
                for (int i = 0; i < _currentRoad.RoadParts.Length - 1; i++)
                {
                    RoadPart rp = _currentRoad.RoadParts[i];
                    RoadPart rpNext = _currentRoad.RoadParts[i + 1];
                    PointCollection c = new PointCollection();
                    c.Add(new Point(rp.X1, rp.Y1));
                    c.Add(new Point(rp.X2, rp.Y2));
                    c.Add(new Point(rpNext.X2, rpNext.Y2));
                    c.Add(new Point(rpNext.X1, rpNext.Y1));
                    if (Road.IsInPolygon2(new Point(ActiveCar.CurPosX, ActiveCar.CurPosY), c))
                    {
                        rpCurr = rp;
                        rpCurrNext = rpNext;
                        break;
                    }
                }
                //return;
            }

            //y = y0 + r*sin(Fi)
            //x = x0 + r*cos(Fi)
            double step = 15.0;
            double r = elVelocity.Height/2;
            double Fi = Math.Atan2( 
                                    ActiveCar.CurPosX - (rpCurrNext.X1 + rpCurrNext.X2)/2
                                                    , 
                                     ActiveCar.CurPosY - (rpCurrNext.Y1 + rpCurrNext.Y2)/2
                                   );
            Fi -= ActiveCar.CurrentEllipseRotation/(180/Math.PI);
            
            //double newSpeedDeltaX = 0;
            //double newSpeedDeltaY = 0;
            lineVectorMovement.X2 = cnvVelocity.Width / 2 - r*Math.Sin(Fi);//e.CumulativeManipulation.Translation.X;
            lineVectorMovement.Y2 = cnvVelocity.Height / 2 - r * Math.Cos(Fi);//e.CumulativeManipulation.Translation.Y;
            CalculateSpeeds();
            moveTimer.Start();
        }

        void moveTimer_Tick(object sender, EventArgs e)
        {
            //CalculateResultVectorAndDrawCarMovement(ActiveCar);
            //ActiveCar.SpeedDeltaX = 0;
            //ActiveCar.SpeedDeltaY = 0;

            //Thread.Sleep(500);
            //tbSpeed.Text = (Fi * (180 / Math.PI)).ToString();
            //click next
            Car1_ManipulationCompleted(this, new ManipulationCompletedEventArgs());
            moveTimer.Stop();
        }
        private void btnNext_Click(object sender, RoutedEventArgs e)
        {
            //Car1_ManipulationCompleted(this, new ManipulationCompletedEventArgs());
        }
        
        private void CalculateResultVectorAndDrawCarMovement(Car car)
        {

            Storyboard sb = new Storyboard();

            car.CreateStoryboardAndMakeNextStep(sb);
            sb.Completed += new EventHandler(sb_Completed);
            sb.Begin();
        }

        
        void sb_Completed(object sender, EventArgs e)
        {

            if (Util.IsFinishCrossed(ActiveCar.CurPosX, ActiveCar.CurPosY, ActiveCar.PrevPosX, ActiveCar.PrevPosY,
                                     _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].X1,
                                     _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].Y1,
                                     _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].X2,
                                     _currentRoad.RoadParts[_currentRoad.RoadParts.Length - 1].Y2))
            {
                MessageBox.Show("Race is over!!!");
                btnReset_Click(this, null);
                return;
            }

            if (!_currentRoad.IsInTrack(new Point(ActiveCar.CurPosX, ActiveCar.CurPosY)))
            {
                tbOut.Visibility = Visibility.Visible;
                ActiveCar.Speed = 0;
                ActiveCar.OutOfTrack = true;

                Storyboard sbMoveBack = new Storyboard();
                ActiveCar.MoveCarY(sbMoveBack, ActiveCar.CurPosY - ActiveCar.Height/2, ActiveCar.PrevPosY - ActiveCar.Height/2);
                ActiveCar.MoveCarX(sbMoveBack, ActiveCar.CurPosX - ActiveCar.Width/2, ActiveCar.PrevPosX - ActiveCar.Width/2);
                sbMoveBack.Completed += new EventHandler(sbMoveBack_Completed);
                sbMoveBack.Begin();
                return;
            }
            else
            {
                ActiveCar.OutOfTrack = false;
                tbOut.Visibility = Visibility.Collapsed;
            }
            //cnvVelocity.Visibility = Visibility.Visible;
            SwitchCars();

        }
        private void  SwitchCars()
        {
            //ActiveCar = carDefault;
            //return;
            if (CarsOnTrack.Count <= 1)
                return;
            int index = CarsOnTrack.IndexOf(ActiveCar);
            index++;
            if (index == CarsOnTrack.Count)
                index = 0;
            Car newActiveCar = CarsOnTrack[index];
            Storyboard sbRotateEllipse = new Storyboard();
            ActiveCar.RotateEllipse(sbRotateEllipse, ActiveCar.CurrentEllipseRotation, newActiveCar.CurrentEllipseRotation);
            
            ActiveCar = newActiveCar;
            sbRotateEllipse.Begin();
            sbRotateEllipse.SeekAlignedToLastTick(new TimeSpan(0,0,0,1));
            if (ActiveCar.IsCPU)
                MakeComputerStep();
        }

        void sbMoveBack_Completed(object sender, EventArgs e)
        {
            SwitchCars();
            
        }

        private bool isManipulating = false;
        Ellipse carPupka = new Ellipse();
        private void Car1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
        {
            lineResultMovement = new Line();
            cnvVelocity.Children.Add(lineVectorMovement);
            cnvVelocity.Children.Add(pupka);
            
            cnvMap.Children.Add(lineResultMovement);
            cnvMap.Children.Add(carPupka);
            lineResultMovement.Stroke = new SolidColorBrush(Color.FromArgb(200, 0, 250, 0));
            lineResultMovement.StrokeThickness = 6;
            pupka.Fill = new SolidColorBrush(Color.FromArgb(55, 255, 0, 0));
            pupka.Width = 20;
            pupka.Height = 20;
            pupka.SetValue(Canvas.LeftProperty, cnvVelocity.Width / 2);
            pupka.SetValue(Canvas.TopProperty, cnvVelocity.Height / 2);
            //lineVectorMovement = new Line();
            //lineVectorMovement.StrokeThickness = 10;
            //lineVectorMovement.Stroke = new SolidColorBrush(Color.FromArgb(40, 255, 0, 0));
            //lineVectorMovement.StrokeEndLineCap = PenLineCap.Round;
            cnvMap.UseLayoutRounding = false;

            isManipulating = true;
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
            {
                while (isManipulating)
                {
                    Thread.Sleep(200);
                    Dispatcher.BeginInvoke(delegate
                                               {
                                                   lineResultMovement.X1 = ActiveCar.CurPosX;
                                                   lineResultMovement.X2 = ActiveCar.NextPosX;

                                                   lineResultMovement.Y1 = ActiveCar.CurPosY;
                                                   lineResultMovement.Y2 = ActiveCar.NextPosY;
                                                   lineResultMovement.StrokeEndLineCap = PenLineCap.Round;
                                                   carPupka.Width = 20;
                                                   carPupka.Height = 20;

                                                   carPupka.SetValue(Canvas.LeftProperty, -carPupka.Width / 2 + lineResultMovement.X2);
                                                   carPupka.SetValue(Canvas.TopProperty, -carPupka.Height / 2 + lineResultMovement.Y2);

                                                   if (ActiveCar.Breaking)
                                                   {
                                                       lineResultMovement.Stroke =
                                                           new SolidColorBrush(Color.FromArgb(200, 150, 0, 0));
                                                       carPupka.Fill = new SolidColorBrush(Color.FromArgb(55, 255, 0, 0));
                                                       pupka.Fill = new SolidColorBrush(Color.FromArgb(200, 150, 0, 0));
                                                   }
                                                   else
                                                   {
                                                       lineResultMovement.Stroke =
                                                           new SolidColorBrush(Color.FromArgb(200, 0, 150, 0));
                                                       carPupka.Fill = new SolidColorBrush(Color.FromArgb(55, 0, 255, 0));
                                                       pupka.Fill = new SolidColorBrush(Color.FromArgb(200, 0, 150, 0));
                                                   }
                                               });
                }

            }));
        }

        //private double currentEllipseRotation = 0;

        Line lineResultMovement = new Line();
        Line lineVectorMovement = new Line();
        Ellipse pupka = new Ellipse();
        private void Car1_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            lineVectorMovement.X1 = cnvVelocity.Width / 2;
            lineVectorMovement.Y1 = cnvVelocity.Height / 2;

            lineVectorMovement.X2 = cnvVelocity.Width / 2 + e.CumulativeManipulation.Translation.X;
            lineVectorMovement.Y2 = cnvVelocity.Height / 2 + e.CumulativeManipulation.Translation.Y;

            CalculateSpeeds();
        }

        private void CalculateSpeeds()
        {
            ActiveCar.SpeedDeltaY = (int) ((lineVectorMovement.Y1 - lineVectorMovement.Y2)/15.0);
            ActiveCar.SpeedDeltaX = (int) ((lineVectorMovement.X1 - lineVectorMovement.X2)/15.0);

            var rOfEl = elVelocity.Height/2;
            if (
                Math.Sqrt(Math.Pow(lineVectorMovement.X1 - lineVectorMovement.X2, 2) +
                          Math.Pow(lineVectorMovement.Y1 - lineVectorMovement.Y2, 2)) > rOfEl)
            {
                double angle =
                    Math.Atan2(lineVectorMovement.Y1 - lineVectorMovement.Y2,
                               lineVectorMovement.X1 - lineVectorMovement.X2) + Math.PI;
                lineVectorMovement.X2 = lineVectorMovement.X1 + rOfEl*Math.Cos(angle);
                lineVectorMovement.Y2 = lineVectorMovement.Y1 + rOfEl*Math.Sin(angle);
            }

            //apply rotation correction

            double angleRot = Math.Atan2(lineVectorMovement.Y1 - lineVectorMovement.Y2,
                                         lineVectorMovement.X1 - lineVectorMovement.X2) + Math.PI;
            double r =
                Math.Sqrt(Math.Pow(lineVectorMovement.X1 - lineVectorMovement.X2, 2) +
                          Math.Pow(lineVectorMovement.Y1 - lineVectorMovement.Y2, 2));

            angleRot -= ActiveCar.CurrentEllipseRotation/(180.0/Math.PI);
            lineVectorMovement.X2 = lineVectorMovement.X1 + r*Math.Cos(angleRot);
            lineVectorMovement.Y2 = lineVectorMovement.Y1 + r*Math.Sin(angleRot);

            pupka.SetValue(Canvas.LeftProperty, -pupka.Width/2 + lineVectorMovement.X2);
            pupka.SetValue(Canvas.TopProperty, -pupka.Height/2 + lineVectorMovement.Y2);

            tbCurrentSpeed.Text = "Vx,Vy" + ActiveCar.SpeedDeltaX.ToString() + ";" +
                                  ActiveCar.SpeedDeltaY.ToString() + ";Total:" + (int) ActiveCar.SpeedDelta;

        }

        private void Car1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            isManipulating = false; // this will stop the thread that draws resulting vector
            tbCurrentSpeed.Text = ActiveCar.Speed.ToString();
            CalculateResultVectorAndDrawCarMovement(ActiveCar);
            cnvMap.Children.Remove(lineResultMovement);
            cnvVelocity.Children.Remove(lineVectorMovement);
            cnvVelocity.Children.Remove(pupka);
            cnvMap.Children.Remove(carPupka);
        }


        private void cnvMap_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {

        }

        private void btnReset_Click(object sender, RoutedEventArgs e)
        {
            Storyboard sb = new Storyboard();
            double curPosY = ActiveCar.Top;
            double curPosX = ActiveCar.Left;
            foreach (var car in CarsOnTrack)
            {
                car.MoveCarY(sb, curPosY, car.InitPosY);
                car.MoveCarX(sb, curPosX, car.InitPosY);
                car.RotateCar(sb, ActiveCar.Angle, 0);
                car.SpeedDeltaX = 0;
                car.SpeedDeltaY = 0;
                car.PrevAngle = 0;
                car.Angle = 0;
                car.Speed = 0;
                car.CurrentEllipseRotation = 0;
                //car.Top = (System.Double)_currentRoad.Car1StartPositionY;
                //car.Left = (System.Double)_currentRoad.Car1StartPositionX;
            }
            
            ActiveCar.RotateEllipse(sb, ActiveCar.CurrentEllipseRotation, 0);
            SwitchCars();
            sb.Begin();

        }

        




    }
}